dojo.provide('ph.bluetooth');
dojo.require('ph.event.Event');

ph.require('javax.bluetooth.LocalDevice');
ph.require('javax.bluetooth.DiscoveryAgent');

dojo.declare('ph.bluetooth._DiscoveryAgent', null, {
  constructor: function() {
    this._agent     = Packages.javax.bluetooth.LocalDevice.getLocalDevice().getDiscoveryAgent();
    this._GIAC      = Packages.javax.bluetooth.DiscoveryAgent.GIAC;
    this._running   = false;
    this._inquiring = false;
    this._lastFind  = null;
    
    this._listener  = new JavaAdapter(javax.bluetooth.DiscoveryListener, {
      deviceDiscovered: dojo.hitch(this, this._deviceDiscovered),
      inquiryCompleted: dojo.hitch(this, this._inquiryCompleted)
    });
    
    this.start = sync(this._run);
  },
  
  _deviceDiscovered: function(device) {
    var id = ph.str(device.getBluetoothAddress());
    this.onDeviceDiscovered(id);
    this._lastFind = new Date;
  },
  
  _inquiryCompleted: function(type) {
    this._inquiring = false;
    if (this._lastFind) this._lastFind = new Date;
    this.onInquiryCompleted();
  },
  
  _run: function(interval) {
    if (this._running) return;
    
    this._running = true;
    var runOnce = !interval;
    
    spawn(dojo.hitch(this, function() {
      do {
        if (!this._inquiring && (!interval || !this._lastFind || new Date - this._lastFind >= interval)) {
          this._inquiring = true;
          this._lastFind = null;
          this.onInquiryStart();
          this._agent.startInquiry(this._GIAC, this._listener);
        }
        
        java.lang.Thread.sleep(1000);
      } while (this._inquiring || !runOnce);
      
      this._running = false;
    }))
  },

  onInquiryStart: function(evt) {},
  onInquiryCompleted: function(evt) {},	
  onDeviceDiscovered: function(evt) {},	
});

dojo.declare('ph.bluetooth._Scanner', null, {
  constructor: function() {
    this._agent    = javax.bluetooth.LocalDevice.getLocalDevice().getDiscoveryAgent();
    this._GIAC     = javax.bluetooth.DiscoveryAgent.GIAC;
    this._known    = {};
    this._new      = {};
    this._running  = false;
    this._runOnce  = false;
    this._scanning = false;
    this._lastFind = null;
    
    this._listener = new JavaAdapter(javax.bluetooth.DiscoveryListener, {
      deviceDiscovered: dojo.hitch(this, this._deviceDiscovered),
      inquiryCompleted: dojo.hitch(this, this._inquiryCompleted)
    });
    
    this.run = sync(this._run);
  },
  
  _deviceDiscovered: function(device) {
    var id = ph.str(device.getBluetoothAddress());
    this._new[id] = {
      id: id,
      name: this._known[id] ? this._known[id].name : ph.str(device.getFriendlyName(false))
    };
    
    if (!(id in this._known)) this.onAppear(new ph.event.Event('appear', this._new[id]));
    
    this._lastFind = new Date;
  },
  
  _inquiryCompleted: function(type) {
    for (id in this._known) {
      if (!(id in this._new)) this.onDisappear(new ph.event.Event('disappear', this._known[id]));
    }
    
    this._known    = this._new;
    this._new      = {};
    this._scanning = false;
    
    if (this._lastFind) this._lastFind = new Date;
    if (this._runOnce) this._running = false;
    
    spawn(dojo.hitch(this, function() {
      this.onInquiryEnd(new ph.event.Event('inquiry_end'));
    }));
  },
  
  _run: function(interval) {
    if(this._running) return;
    
    this._running = true;
    this._runOnce = !interval;
    
    spawn(dojo.hitch(this, function() {
      do {
        if (!this._scanning && (!interval || !this._lastFind || new Date - this._lastFind >= interval)) {
          this._scanning = true;
          this._lastFind = null;
          this.onInquiryStart(new ph.event.Event('inquiry_start'));
          this._agent.startInquiry(this._GIAC, this._listener);
        }
        
        java.lang.Thread.sleep(1000);
      } while(this._running); // NOT thread-safe: two runOnce's can get intertwined
    }))
  },
  
  onAppear: function(evt) {},
  onDisappear: function(evt) {},
  onInquiryStart: function(evt) {},
  onInquiryEnd: function(evt) {},
});

(function() {
  var scanner = null;
  ph.bluetooth.scanner = function() {
    if (!scanner) scanner = new ph.bluetooth._Scanner();
    return scanner;
  };
  
  var discovery = null;
  ph.bluetooth.__defineGetter__('discovery', function() {
    if (!discovery) discovery = new ph.bluetooth._DiscoveryAgent();
    return discovery;
  });
})();